﻿using System;
using System.Collections.Generic;

namespace Core.Tools
{
    [Serializable]
    public class Table
    {
        private const int InitializeSize = 3; // 初始字段数量
        private const int UnUsedHashCode = -1; // 哈希值为此的 Entry 表示未使用
        private const int FreedHashCode = -2; // 哈希值为此的 Entry 表示被删除

        private static readonly object NullValue = new object(); // 表示 null 值在内部保存的值，因为 null 在内部有其他意义。

        private int[] Buckets; // 通过 Key 的哈希值的余数作为索引查找 Entries 索引。
        private Entry[] Entries; // 存放 Key 信息。
        private object[,] Values; // 存放 Value 信息 [RowIndex, ColumnIndex]。

        private int InternalColumnCount; // 当前字段总数

        private int InternalRowsCount; // 当前总行数

        private int InternalRowIndex; // 当前所在行

        [Serializable]
        private struct Entry
        {
            public int HashCode; // 哈希值。
            public int Next; // 下一个于此哈希值余数相同的元素。
            public string Key; // Key 值。
        }

        public Table()
        {
            InternalRowsCount = 0;

            InternalRowIndex = -1;

            InternalColumnCount = 0;
        }

        public void Clear()
        {
            if (InternalRowsCount != 0)
            {
                for (int i = 0; i < Buckets.Length; i++) Buckets[i] = -1;

                Array.Clear(Entries, 0, InternalColumnCount);


                for (int i = 0; i < InternalRowsCount; i++)
                {
                    for (int j = 0; j < InternalColumnCount; j++)
                    {
                        Values[i, j] = null;
                    }
                }

                InternalRowsCount = 0;

                InternalRowIndex = -1;

                InternalColumnCount = 0;
            }
        }

        public int GetOrdinal(string Name)
        {
            if (Name == null)
            {
                throw new NullReferenceException("Name");
            }

            if (Buckets != null)
            {
                int HashCode = Name.GetHashCode() & int.MaxValue;

                for (int i = Buckets[HashCode % Buckets.Length]; i >= 0; i = Entries[i].Next)
                {
                    if (HashCode == Entries[i].HashCode && Name == Entries[i].Key)
                    {
                        return i;
                    }
                }
            }

            return -1;
        }

        public string GetName(int Index)
        {
            if (Index >= 0 && Index < ColumnsCount)
            {
                return Entries[Index].Key;
            }

            throw new IndexOutOfRangeException("Index");
        }

        public bool TryGetValue(string Name, out object Value)
        {
            if (Name == null)
            {
                throw new NullReferenceException("Name");
            }

            if (InternalRowIndex >= InternalRowsCount)
            {
                throw new IndexOutOfRangeException("RowIndex");
            }

            var Index = GetOrdinal(Name);

            if (Index == -1)
            {
                Value = null;

                return false;
            }

            Value = Values[InternalRowIndex, Index];

            if (Value == null)
            {
                return false;
            }

            if (Value == NullValue)
            {
                Value = null;
            }

            return true;
        }

        public object this[string Name]
        {
            get
            {
                object Result;

                if (TryGetValue(Name, out Result))
                {
                    return Result;
                }

                throw new IndexOutOfRangeException("Name");
            }
            set
            {
                if (InternalRowsCount == 0)
                {
                    throw new Exception("没有任何行数据，请先添加行数据。");
                }

                Insert(Name, value, false);
            }
        }

        public object this[int Index]
        {
            get
            {
                if (Index >= InternalColumnCount)
                {
                    throw new IndexOutOfRangeException("Index");
                }

                if (InternalRowIndex >= InternalRowsCount)
                {
                    throw new IndexOutOfRangeException("RowIndex");
                }

                object Result = Values[InternalRowIndex, Index];

                if (Result == NullValue)
                {
                    Result = null;
                }

                return Result;
            }
            set
            {
                if (InternalRowsCount == 0)
                {
                    throw new Exception("没有任何行数据，请先添加行数据。");
                }

                if (Index > InternalColumnCount)
                {
                    throw new IndexOutOfRangeException("Index");
                }

                if (Index >= Entries.Length)
                {
                    int NewCount = Index * 2;

                    if ((uint)NewCount > ArrayHelper.MaxPrimeArrayLength && ArrayHelper.MaxPrimeArrayLength > Index)
                    {
                        Resize(Values.GetLength(0), ArrayHelper.MaxPrimeArrayLength);
                    }
                    else
                    {
                        Resize(Values.GetLength(0), ArrayHelper.GetPrime(NewCount));
                    }
                }

                if (value == null)
                {
                    value = NullValue;
                }

                Values[InternalRowsCount - 1, Index] = value;

                if (Index == InternalColumnCount)
                {
                    ++InternalColumnCount;
                }
            }
        }

        public bool Read()
        {
            ++InternalRowIndex;

            return InternalRowIndex < InternalRowsCount;
        }

        public IEnumerable<string> Columns
        {
            get
            {
                return new CollectionEnumerable<string>((Index) =>
                {
                    return Entries[Index].Key;

                }, InternalColumnCount, true);
            }
        }

        public int ColumnsCount
        {
            get { return InternalColumnCount; }
        }

        public int RowsCount
        {
            get
            {
                return InternalRowsCount;
            }
        }

        public void Add(string Name, object Value)
        {
            if (InternalRowsCount == 0)
            {
                throw new Exception("没有任何行数据，请先添加行数据。");
            }

            Insert(Name, Value, true);
        }

        public void Next()
        {
            if (Buckets == null)
            {
                Initialize();
            }

            if (InternalRowsCount == Values.GetLength(0))
            {
                int NewRows = InternalRowsCount * 2;

                if (NewRows > ArrayHelper.MaxPrimeArrayLength && ArrayHelper.MaxPrimeArrayLength > InternalRowsCount)
                {
                    Resize(ArrayHelper.MaxPrimeArrayLength, Entries.Length);
                }
                else
                {
                    Resize(ArrayHelper.GetPrime(NewRows), Entries.Length);
                }
            }

            ++InternalRowsCount;
        }

        private void Resize(int Rows, int Columns)
        {
            if (Columns == Entries.Length)
            {
                goto ReRows;
            }

            int[] NewBuckets = new int[Columns];

            for (int i = 0; i < NewBuckets.Length; i++)
            {
                NewBuckets[i] = UnUsedHashCode;
            }

            Entry[] NewEntries = Entries;

            Array.Resize(ref NewEntries, Columns);

            for (int i = 0; i < InternalColumnCount; i++)
            {
                if (NewEntries[i].HashCode >= 0)
                {
                    int TargetBucket = NewEntries[i].HashCode % Columns;
                    NewEntries[i].Next = NewBuckets[TargetBucket];
                    NewBuckets[TargetBucket] = i;
                }
            }

            Buckets = NewBuckets;
            Entries = NewEntries;

            ReRows:

            object[,] NewValue = new object[Rows, Columns];

            Rows = Math.Min(Rows, Values.GetLength(0));
            Columns = Math.Min(Columns, Values.GetLength(1));

            for (int i = 0; i < Rows; i++)
            {
                for (int j = 0; j < Columns; j++)
                {
                    NewValue[i, j] = Values[i, j];
                }
            }

            Values = NewValue;
        }

        private void Initialize()
        {
            Buckets = new int[InitializeSize];

            for (int i = 0; i < InitializeSize; i++)
            {
                Buckets[i] = UnUsedHashCode;
            }

            Entries = new Entry[InitializeSize];
            Values = new object[1, InitializeSize];
        }

        private void Insert(string Name, object Value, bool Add)
        {
            if (Name == null)
            {
                throw new NullReferenceException("Name");
            }

            if (Value == null)
            {
                Value = NullValue;
            }

            int RowIndex = InternalRowsCount - 1;

            int HashCode = Name.GetHashCode() & int.MaxValue;
            int TargetBucket = HashCode % Buckets.Length;

            for (int i = Buckets[TargetBucket]; i >= 0; i = Entries[i].Next)
            {
                if (Entries[i].HashCode == HashCode && Name == Entries[i].Key)
                {
                    if (Add && Values[RowIndex, i] != null)
                    {
                        throw new Exception("Repeated Name!");
                    }

                    Values[RowIndex, i] = Value;

                    return;
                }
            }

            if (InternalColumnCount == Entries.Length)
            {
                int NewColumns = InternalColumnCount * 2;

                if ((uint)NewColumns > ArrayHelper.MaxPrimeArrayLength && ArrayHelper.MaxPrimeArrayLength > InternalColumnCount)
                {
                    Resize(Values.GetLength(0), ArrayHelper.MaxPrimeArrayLength);
                }
                else
                {
                    Resize(Values.GetLength(0), ArrayHelper.GetPrime(NewColumns));
                }

                TargetBucket = HashCode % Buckets.Length;
            }

            Entries[InternalColumnCount].HashCode = HashCode;
            Entries[InternalColumnCount].Next = Buckets[TargetBucket];
            Entries[InternalColumnCount].Key = Name;

            Values[RowIndex, InternalColumnCount] = Value;

            Buckets[TargetBucket] = InternalColumnCount;

            ++InternalColumnCount;
        }

        public void Reset()
        {
            InternalRowIndex = -1;
        }
    }
}